package abel.wlmr.model.type;

import org.joda.time.DateTime;

import abel.util.StdBcdNumber;
import abel.wlmr.model.WlmrException;
import io.netty.buffer.ByteBuf;

/**
 * B.5.12.2.5.7 ERC14：终端停/上电事件
 * 事件记录的数据格式见表 B.191。
 * 表B.191 终端停/上电事件记录数据单元格式
 * 数据内容 数据格式 字节数
 * ERC=14 BIN 1
 * 长度 Le BIN 1
 * 停电发生时间：分时日月年 见附录 C.15 5
 * 上电时间：分时日月年 见附录 C.15 5
 * 
 * @author Abel
 *
 */
public class Erc14Body extends ErcBaseBody {

    private static final short FIXED_LENGTH = 5 + 5;

    /**
     * 停电发生时间：分时日月年 见附录 C.15 5
     */
    private StdBcdNumber offTime;

    /**
     * 上电时间：分时日月年 见附录 C.15 5
     */
    private StdBcdNumber openTime;

    public Erc14Body(short guessedLen) {
        super(guessedLen, (short) 14, FIXED_LENGTH);
    }

    public Erc14Body() {
        this((short) -1);
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.AppUnitBody#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        return MIN_LENGTH + FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.ErcBaseBody#appendBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        if (offTime == null || offTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }
        if (openTime == null || openTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }

        boolean offTimeOmit = false;
        boolean openTimeOmit = false;

        try {
            offTime.toMeterC15Joda();
        } catch (IllegalArgumentException ex) {
            offTimeOmit = true;
        }

        try {
            openTime.toMeterC15Joda();
        } catch (IllegalArgumentException ex) {
            openTimeOmit = true;
        }

        if (offTimeOmit && openTimeOmit) {
            throw new IllegalArgumentException("both offTime and openTime can't be omitted");
        }

        // append erc & le.
        buf.writeByte(erc);
        buf.writeByte(le);

        buf.writeBytes(offTime.toBytes());
        buf.writeBytes(openTime.toBytes());

        return buf;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.ErcBaseBody#parseBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (checkBuffer(buf) < 0) {
            return -1;
        }

        buf.skipBytes(MIN_LENGTH);

        offTime = new StdBcdNumber();
        offTime.populateBytes(buf, 5);

        openTime = new StdBcdNumber();
        openTime.populateBytes(buf, 5);

        // verify off/open time. may throw IAE
        try {
            DateTime dtOff = offTimeJoda();
            DateTime dtOpen = openTimeJoda();
            if (dtOff == null && dtOpen == null) {
                throw new WlmrException("off/open time can't be both null at the same time.");
            }
        } catch (IllegalArgumentException ex) {
            throw new WlmrException("invalid off / open time in ERC-14.", ex);
        }

        return MIN_LENGTH + FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{offTime:%s, openTime:%s}", offTime, openTime);
    }

    /**
     * 停电发生时间：分时日月年 见附录 C.15 5
     * 
     * @return the offTime
     */
    public StdBcdNumber getOffTime() {
        return offTime;
    }

    /**
     * Return null if all bytes are 0.
     * 
     * @return
     * @throws -
     *             {@link IllegalArgumentException} if fail to parse
     */
    public DateTime offTimeJoda() {
        for (byte b1 : offTime.toBytes()) {
            if (b1 != 0) {
                return offTime.toMeterC15Joda();
            }
        }
        return null;
    }

    /**
     * 停电发生时间：分时日月年 见附录 C.15 5
     * 
     * @param offTime
     *            the offTime to set
     */
    public void setOffTime(StdBcdNumber offTime) {
        if (offTime == null || offTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }

        // may throw IAE
//        offTime.toMeterC15Joda();

        this.offTime = offTime;
    }

    /**
     * 上电时间：分时日月年 见附录 C.15 5
     * 
     * @return the openTime
     */
    public StdBcdNumber getOpenTime() {
        return openTime;
    }

    /**
     * Returns null if all bytes are 0.
     * 
     * @return
     * @throws -
     *             {@link IllegalArgumentException} invalid date format.
     */
    public DateTime openTimeJoda() {
        for (byte b1 : openTime.toBytes()) {
            if (b1 != 0) {
                return openTime.toMeterC15Joda();
            }
        }
        return null;
    }

    /**
     * 上电时间：分时日月年 见附录 C.15 5
     * 
     * @param openTime
     *            the openTime to set
     */
    public void setOpenTime(StdBcdNumber openTime) {
        if (openTime == null || openTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }

        // may throw IAE
//        openTime.toMeterC15Joda();

        this.openTime = openTime;
    }
}
